package mips.instructions;

/**
 * <code>LW</code> instruction<br/>
 * Load Word</br>
 * @author jnmartin84@gmail.com
 */
public class LW extends Instruction {

	private static final LW INSTANCE = new LW();
	private static final String INSTRUCTION_NAME = "LW";

	private LW(){}

	public static final LW getInstance() {
		return INSTANCE;
	}

	/**
	 * <b>Format:</b><br/>
	 * LW rt, offset(base)<br/><br/>
	 * <b>Description:</b><br/>
	 * The 16-bit offset is sign-extended and added to the contents of general<br/>
	 * register base to form a virtual address. The contents of the word at the<br/>
	 * memory location specified by the effective address are loaded into general<br/>
	 * register rt. If either of the two least-significant bits of the effective<br/> 
	 * address is non-zero, an address error exception occurs.<br/><br/>
	 * <b>Operation:</b><br/>
	 * T: vAddr &larr; ((offset<sub>15</sub>)<sup>16</sup> || offset<sub>15&hellip;0</sub>) + GPR[base]<br/>
	 * (pAddr, uncached) &larr; AddressTranslation (vAddr, DATA)<br/>
	 * pAddr &larr; pAddr<sub>PSIZE-1&hellip;3</sub> || (pAddr<sub>2&hellip;0</sub> xor (ReverseEndian || 0<sup>2</sup>))<br/>
	 * mem &larr; LoadMemory (uncached, WORD, pAddr, vAddr, DATA)<br/>
	 * byte &larr; vAddr<sub>2&hellip;0</sub> xor (BigEndianCPU || 0<sup>2</sup>)<br/>
	 * GPR[rt] &larr; mem<sub>31+8*byte&hellip;8*byte</sub>
	 */
	@Override
	public final void execute(final int instruction) throws mips.exceptions.AddressErrorException,
															mips.exceptions.BusErrorException,
															mips.exceptions.TlbInvalidException,
															mips.exceptions.TlbRefillException {
		
		final int rs = (instruction >> 21) & 0x0000001F;
		final int rt = (instruction >> 16) & 0x0000001F;
		final int immed = Instruction.signExtendH(instruction & 0x0000FFFF);
		
		// double-check this works right!
		mips.R4300i.GPR[rt] = n64.MemoryManager.readWord((int)(mips.R4300i.GPR[rs] & 0x00000000FFFFFFFFL) + immed);

		mips.R4300i.PC = mips.R4300i.nPC;
		mips.R4300i.nPC = mips.R4300i.PC + 4;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String emit(final int instruction) {
		
		final int rs = (instruction >> 21) & 0x0000001F;
		final int rt = (instruction >> 16) & 0x0000001F;
		final int immed = Instruction.signExtendH(instruction & 0x0000FFFF);
		
		return	"		// double-check this works right!\n" +
				"		mips.CPU.GPR["+rt+"] = n64.MemoryManager.readWord((int)(mips.CPU.GPR["+rs+"] & 0x00000000FFFFFFFFL) + "+immed+");\n" + 
				"		\n" + 
				"		mips.CPU.PC = mips.CPU.nPC;\n" + 
				"		mips.CPU.nPC = mips.CPU.PC + 4;\n";
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName(final int instruction) {
		return getName();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName() {
		return INSTRUCTION_NAME;
	}
}